import os, sys

class List:
    def lib_path(): return __file__
    # def __init__(self):
    #     pass
    @staticmethod
    def mix(list_list, method):
        return [method(*[l[i] for l in list_list])  for i in range(len(list_list[0]))]
    @staticmethod
    def cook(l, change_proc):
        return [change_proc(e) for e in l]
    @staticmethod
    def str_deep(l):
        return "\n".join([ str(e) for e in l])
    @staticmethod
    def l_absorb_r(l, r):
        List.l_absorb_r_when(l, r, lambda e:True)
    @staticmethod
    def l_absorb_r_when(l, r, check_element_good):
        change = False
        for e in r:
            if check_element_good(e):
                if e not in l:
                    change = True
                    l.append(e)
        return l
    @staticmethod
    def find_e(l, f_cond):
        index = List.find_i(l, f_cond)
        return l[ index ] if index != None else None
    @staticmethod
    def find_i(l, f_cond):
        for i, e in enumerate(l):
            if f_cond(e):
                return i
        return None
    @staticmethod
    def find_best_i(l, set_best):
        base = l[0]
        best_index = 0
        for i, e in enumerate(l):
            if set_best(base, e):
                best_index = i
        return best_index
    @staticmethod
    def find_best_e(l, set_best):
        return l[ List.find_best_i(l, set_best) ]
    @staticmethod
    def pop_n(l, n):
        for i in range(n):
            l.pop()
    @staticmethod
    def l_include_r(l, sub_l):
        for e in sub_l:
            if e not in l:
                return False
        return True
    @staticmethod
    def same(this, that, is_same):
        if len(this) != len(that):
            return False
        for i in range(len(this)):
            if not is_same(this[i], that[i]):
                return False
        return True



class Dict:
    # @staticmethod
    # def for_kv(d, call_back):
    #     for k in d:
    #         v = d[k]
    #         call_back(k, v)
    
    @staticmethod
    def find_key(d, value):
        for k in d:
            if id(d[k]) == id(value):
                return k
        return None
    @staticmethod
    def append(d, key, value):
        if key not in d.keys():
            d[key] = []
        d[key].append(value)

    @staticmethod
    def get(d, key):
        if key in d.keys():
            return d[key]
        return None
    @staticmethod
    def cook(d, call_back):
        #change value
        new_d = {}
        for key in d:
            value = d[key]
            new_key, new_value = call_back(key, value)
            new_d[new_key] = new_value
        return new_d
    @staticmethod
    def json_str(d, name):
        tab = " "*4
        if isinstance(name, str):
            name = "'{name}'".format(**locals())
        else:
            name = str(name)
        if isinstance(d, dict):
            line_list = ["{name} : {{".format(**locals())]
            for key in d:
                line_list.append( "\n".join( [tab+line for line in Dict.json_str(d[key], key).split("\n")] ))
            line_list.append("}")
            return "\n".join(line_list)
        elif isinstance(d, list):
            line_list = ["{name} : [".format(**locals())]
            for i, e in enumerate(d):
                line_list.append( "\n".join( [tab+line for line in Dict.json_str(d[i], i).split("\n")] ))
            line_list.append("]")
            return "\n".join(line_list)
        else:
            value = str(d)
            return "{name} : '{value}'".format(**locals())
    # @staticmethod
    # def deep_copy(d):
    #     new_d = {}
    #     for key in d:
    #         value = d[key]
    #         new_d[key] = value
    #     return new_d